package api

import (
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/goccy/go-json"
	"go-caipu/pkg/api/helper"
	"go-caipu/pkg/api/pluginproxy"
	"go-caipu/pkg/infra/log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"strings"
	"sync"
	"time"
)

var ComProxyTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).DialContext,
	TLSHandshakeTimeout: 10 * time.Second,
}

// JoinURLFragments joins two ApiURL fragments into only one ApiURL string.
func JoinURLFragments(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")

	if len(b) == 0 {
		return a
	}

	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}
func ReverseProxyReq(log log.Logger, proxyPath, version, ComAPIUrl, token string) *httputil.ReverseProxy {
	// 创建一个新的请求
	url, _ := url.Parse(ComAPIUrl)

	director := func(req *http.Request) {
		req.URL.Scheme = url.Scheme
		req.URL.Host = url.Host
		req.Host = url.Host

		req.URL.Path = JoinURLFragments(url.Path, proxyPath)

		// clear cookie headers
		req.Header.Del("Cookie")
		req.Header.Del("Set-Cookie")
		req.Header.Del("Authorization")

		// send the current Grafana version for each request proxied to GCOM
		req.Header.Add("grafana-version", version)

		if token != "" {
			req.Header.Set("Authorization", "Bearer "+token)
		}
	}
	utl := httputil.NewSingleHostReverseProxy(url)
	utl.Director = director
	return utl
}

// ProxyCnetRequest 前端访问代理到main
func (hs *HTTPServer) ProxyCnetRequest(c *gin.Context) {
	proxyPath := c.Param("url")
	proxy := ReverseProxyReq(hs.log, proxyPath, hs.Cfg.BuildVersion, hs.Cfg.PluginCfg.CaiPuComURL, "")
	proxy.Transport = ComProxyTransport
	proxy.ErrorHandler = func(writer http.ResponseWriter, request *http.Request, e error) {
		writer.WriteHeader(http.StatusOK)
		json, _ := json.Marshal(helper.Response{Code: 500, Msg: e.Error(), Success: false})
		writer.Write([]byte(json))
	}
	//以下暂不生效
	proxy.ModifyResponse = func(resp *http.Response) error { // 给 ModifyResponse 赋 回调函数即可
		if resp.StatusCode != http.StatusOK {
			return errors.New("proxy error	")
		}
		return nil
	}
	proxy.ServeHTTP(c.Writer, c.Request)
}

// ProxyPluginRequest 功能未实现
// @description 插件请求转发
func (hs *HTTPServer) ProxyPluginRequest(c *gin.Context) {
	var once sync.Once
	var pluginProxyTransport *http.Transport
	once.Do(func() {
		pluginProxyTransport = &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).DialContext,
			TLSHandshakeTimeout: 10 * time.Second,
		}
	})

	pluginId := c.Param("pluginId")

	plugin, exists := hs.pluginStore.Plugin(c.Request.Context(), pluginId)
	if !exists {
		helper.JsonError(c, http.StatusNotFound, helper.Error(errors.New("plugin not found, no installed plugin with that id")))
		return
	}
	proxyPath := getProxyPath(c)

	p, err := pluginproxy.NewPluginProxy(plugin.Routes, c, proxyPath, pluginProxyTransport, hs.dataSourceService, pluginId)
	if err != nil {
		helper.JsonError(c, http.StatusInternalServerError, helper.Error(errors.New("failed to create plugin proxy")))
	}

	p.HandleRequest()
}

var pluginProxyPathRegexp = regexp.MustCompile(`^\/api\/plugin-proxy\/([\w\-]+)\/?`)

func extractProxyPath(originalRawPath string) string {
	return pluginProxyPathRegexp.ReplaceAllString(originalRawPath, "")
}

// getProxyPath 获取代理的地址
func getProxyPath(c *gin.Context) string {
	return extractProxyPath(c.Request.URL.EscapedPath())
}
